package top.deepdesigner.controller.admin;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import top.deepdesigner.captcha.CaptchaCodeManager;
import top.deepdesigner.pojo.DtsAdmin;
import top.deepdesigner.service.DtsAdminService;
import top.deepdesigner.service.DtsPermissionService;
import top.deepdesigner.service.DtsRoleService;
import top.deepdesigner.util.*;
import top.deepdesigner.util.Base64;
import top.deepdesigner.util.UUID;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 管理员身份验证控制器<br/>
 * Created with IntelliJ IDEA. <br/>
 * Project Name: shop_manage <br/>
 * Author: duanruiqing <br/>
 * DateTime: 2022/9/20 星期二 14:19:02 <br/>
 */
@RestController
@CrossOrigin("*")
@RequestMapping("/admin/auth")
@Slf4j
public class AdminAuthController {
  @Resource
  private DtsAdminService dtsAdminService;

  @Resource
  private DtsRoleService roleService;

  @Resource
  private DtsPermissionService permissionService;

  @Autowired
  ApplicationContext applicationContext;

  /**
   * 生成验证码
   *
   * @return {@link Object}
   * @throws IOException io异常
   */
  @GetMapping("/captchaImage")
  public Object getCode() throws IOException {
    //生成验证码
    String verifyCode = VerifyCodeUtils.generateVerifyCode(4);
    //验证码表示id
    String uuid = UUID.randomUUID().toString();
    //保存验证码
    boolean b = CaptchaCodeManager.addToCache(uuid, verifyCode, 3);
    if (!b) {
      log.error("【验证码模块】验证码保存异常：{} 验证码为{}", AdminResponseCode.AUTH_CAPTCHA_FREQUENCY, verifyCode);
      return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_FREQUENCY);
    }

    //输出到字节数组流
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    VerifyCodeUtils.outputImage(111, 36, baos, verifyCode);

    try {
      HashMap<String, Object> map = new HashMap<>();
      map.put("img", Base64.encode(baos.toByteArray()));
      map.put("uuid", uuid);
      return ResponseUtil.ok(map);
    } catch (Exception e) {
      return ResponseUtil.serious();
    } finally {
      baos.close();
    }
  }

  /**
   * 登录
   * 方案一、DTO
   * 方案二、直接接收JSON字符串，再用jackson解析
   *
   * @param map 接收参数
   * @return {@link Object}
   */
  @PostMapping("/login")
  public Object login(@RequestBody Map<String, Object> map) {
    //1、接收参数
    System.out.println("map = " + map);
    String username = (String) map.get("username");
    String password = (String) map.get("password");
    String code = (String) map.get("code");
    //Boolean rememberMe = (Boolean) map.get("rememberMe");

    //2、非空校验
    if (StringUtils.isEmpty(username)) {
      log.error("用户名不能为空");
      return AdminResponseUtil.fail(AdminResponseCode.ADMIN_NULL_USERNAME);
    }
    if (StringUtils.isEmpty(password)) {
      log.error("密码不能为空");
      return AdminResponseUtil.fail(AdminResponseCode.ADMIN_NULL_PASSWORD);
    }
    if (StringUtils.isEmpty(code)) {
      log.error("验证码为空:{}", AdminResponseCode.AUTH_CAPTCHA_NULL);
      return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_NULL);
    }
    //2.1 从内存中取出验证码
    String cacheCode = CaptchaCodeManager.getCachedCaptcha((String) map.get("uuid"));
    if (cacheCode == null) {
      log.error("验证码过期{} ", AdminResponseCode.AUTH_CAPTCHA_EXPIRED);
      return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_EXPIRED);
    }
    //2.2、检验验证码
    if (!cacheCode.equalsIgnoreCase(code)) {
      return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_ERROR);
    }
    //3、校验用户名和密码
    Subject subject = SecurityUtils.getSubject();
    //获取准备要返回给前端的session id
    Session session = subject.getSession();

    UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    //if (rememberMe != null) {
    //  token.setRememberMe(rememberMe);
    //}

    try {
      subject.login(token);
    } catch (UnknownAccountException e) {
      return AdminResponseUtil.fail(AdminResponseCode.ADMIN_INVALID_ACCOUNT);
    } catch (IncorrectCredentialsException e) {
      return AdminResponseUtil.fail(AdminResponseCode.ADMIN_INVALID_PASSWORD);
    } catch (LockedAccountException e) {
      return AdminResponseUtil.fail(AdminResponseCode.ADMIN_LOCK_ACCOUNT);
    } catch (AuthenticationException e) {
      return AdminResponseUtil.fail(AdminResponseCode.ADMIN_INVALID_AUTH);
    }
    return ResponseUtil.ok(session.getId());
  }

  @GetMapping("/info")
  public Object info() {
    //1、获取Subject
    Subject subject = SecurityUtils.getSubject();

    //2、获取当前登录的用户
    DtsAdmin dtsAdmin = (DtsAdmin) subject.getPrincipal();

    //3、创建返回对象
    HashMap<String, Object> map = new HashMap<>();
    //设置用户名
    map.put("name", dtsAdmin.getUsername());
    //设置头像
    map.put("avatar", dtsAdmin.getAvatar());

    //4、获取用户角色id，并获取角色和权限
    Integer[] roleIds = dtsAdmin.getRoleIds();
    Set<String> roleIdByIds = roleService.getRoleIdByIds(roleIds);
    //设置角色信息
    map.put("roles", roleIdByIds);

    Set<String> permissionsByRoleId = permissionService.getPermissionsByRoleId(roleIds);
    //System.out.println("permissionsByRoleId = " + permissionsByRoleId);
    //List<String> collect = permissionsByRoleId.stream().map(item -> "GET " + item.replaceAll(":", "/")).collect(Collectors.toList());
    //设置用户权限
    //等待项目开发一定程度再完成权限校验
    Set<String> strings = permissionApi(permissionsByRoleId);
    if (strings.size() == 0) {
      strings = new HashSet<>();
      strings.add("null perms");
    }
    map.put("perms", strings);
    return ResponseUtil.ok(map);
  }

  @PostMapping("/logout")
  public Object logout() {
    Subject subject = SecurityUtils.getSubject();
    try {
      subject.logout();
      log.info("【用户模块】用户退出");
      return ResponseUtil.ok("success");
    } catch (Exception e) {
      return ResponseUtil.fail();
    }
  }

  /**
   * permissionApi转换
   *
   * @param permissionsByRoleId 角色权限id
   * @return {@link Set}<{@link String}>
   */
  private Set<String> permissionApi(Set<String> permissionsByRoleId) {
    List<Permission> permissions = PermissionUtil.listPermission(applicationContext, "top.deepdesigner.controller");
    if (permissionsByRoleId.contains("*")) {
      return permissionsByRoleId;
    }
    //System.out.println("collect = " + collect);
    return permissions
        .stream()
        .filter(item -> permissionsByRoleId.contains(item.getRequiresPermissions().value()[0]))
        .map(Permission::getApi).collect(Collectors.toSet());
  }
}
